import { Injectable, Inject, Scope } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository, FindManyOptions } from "typeorm";
import { Logs, LogLevel, LogType } from "./logs.entity";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
import { Logger } from "winston";
import { REQUEST } from "@nestjs/core";
import { type Request } from "express";

/**
 * 创建日志的数据传输对象
 */
export interface CreateLogDto {
  level: LogLevel;
  logType: LogType;
  message: string;
  context?: string;
  path?: string;
  method?: string;
  statusCode?: number;
  data?: string;
  stack?: string;
  meta?: Record<string, unknown>;
  result?: number;
  duration?: number;
  clientIp?: string;
  userAgent?: string;
  userId?: number;
}

/**
 * HTTP请求日志数据传输对象
 */
export interface CreateHttpLogDto {
  message: string;
  path: string;
  method: string;
  statusCode: number;
  data?: string;
  duration?: number;
  clientIp?: string;
  userAgent?: string;
  userId?: number;
  result?: number;
}

/**
 * 日志查询选项
 */
export interface LogQueryOptions {
  level?: LogLevel;
  logType?: LogType;
  userId?: number;
  startDate?: Date;
  endDate?: Date;
  limit?: number;
  offset?: number;
}

/**
 * 日志服务 - 处理日志相关的业务逻辑
 *
 * 功能包括：
 * - Winston日志记录到数据库
 * - HTTP请求日志记录
 * - 错误日志记录
 * - 日志查询和统计
 * - 用户操作审计
 */
@Injectable({ scope: Scope.REQUEST })
export class LogsService {
  constructor(
    @InjectRepository(Logs)
    private logsRepository: Repository<Logs>,
    @Inject(WINSTON_MODULE_PROVIDER)
    private readonly logger: Logger,
    @Inject(REQUEST)
    private readonly req: Request,
  ) {}

  /**
   * 创建日志记录
   * @param createLogDto 日志数据
   */
  async createLog(createLogDto: CreateLogDto): Promise<Logs> {
    const log = this.logsRepository.create(createLogDto);

    // 自动从当前请求上下文补全 userId（若未显式传入）
    const currentUserId = createLogDto.userId ?? (this.req?.user as any)?.userId;
    if (currentUserId !== undefined && currentUserId !== null) {
      // 补充到实体，确保数据库保存带有 userId
      (log as any).userId = currentUserId;
    }

    // 同时写入Winston文件日志
    this.logger.log({
      level: createLogDto.level,
      message: createLogDto.message,
      context: createLogDto.context,
      meta: {
        ...createLogDto.meta,
        logType: createLogDto.logType,
        path: createLogDto.path,
        method: createLogDto.method,
        statusCode: createLogDto.statusCode,
        duration: createLogDto.duration,
        clientIp: createLogDto.clientIp,
        userAgent: createLogDto.userAgent,
        userId: typeof currentUserId !== "undefined" ? currentUserId : createLogDto.userId,
      },
      stack: createLogDto.stack,
    });

    return await this.logsRepository.save(log);
  }

  /**
   * 创建HTTP请求日志
   * @param httpLogDto HTTP日志数据
   */
  async createHttpLog(httpLogDto: CreateHttpLogDto): Promise<Logs> {
    const level =
      httpLogDto.statusCode >= 500 ? LogLevel.ERROR : httpLogDto.statusCode >= 400 ? LogLevel.WARN : LogLevel.INFO;

    return this.createLog({
      level,
      logType: LogType.HTTP,
      message: httpLogDto.message,
      context: "HTTP",
      path: httpLogDto.path,
      method: httpLogDto.method,
      statusCode: httpLogDto.statusCode,
      data: httpLogDto.data,
      duration: httpLogDto.duration,
      clientIp: httpLogDto.clientIp,
      userAgent: httpLogDto.userAgent,
      userId: httpLogDto.userId,
      result: httpLogDto.result || (httpLogDto.statusCode < 400 ? 0 : 1),
    });
  }

  /**
   * 创建错误日志
   * @param error 错误对象
   * @param context 上下文
   * @param userId 用户ID
   * @param meta 额外元数据
   */
  async createErrorLog(error: Error, context?: string, userId?: number, meta?: Record<string, unknown>): Promise<Logs> {
    return this.createLog({
      level: LogLevel.ERROR,
      logType: LogType.ERROR,
      message: error.message,
      context: context || "Error",
      stack: error.stack,
      userId,
      meta,
      result: 1, // 错误状态
    });
  }

  /**
   * 创建系统日志
   * @param message 日志消息
   * @param level 日志级别
   * @param context 上下文
   * @param meta 元数据
   */
  async createSystemLog(
    message: string,
    level: LogLevel = LogLevel.INFO,
    context?: string,
    meta?: Record<string, unknown>,
  ): Promise<Logs> {
    return this.createLog({
      level,
      logType: LogType.SYSTEM,
      message,
      context: context || "System",
      meta,
      result: 0, // 系统日志默认成功
    });
  }

  /**
   * 查询用户日志
   * @param userId 用户ID
   * @param options 查询选项
   */
  async findUserLogs(userId: number, options?: LogQueryOptions): Promise<Logs[]> {
    // 移除用户验证逻辑，由调用方负责验证用户存在性
    // 注意：调用此方法前请确保userId有效

    const queryOptions: FindManyOptions<Logs> = {
      where: {
        userId,
        ...(options?.level && { level: options.level }),
        ...(options?.logType && { logType: options.logType }),
      },
      relations: {
        user: true,
      },
      order: {
        createdAt: "DESC",
      },
      ...(options?.limit && { take: options.limit }),
      ...(options?.offset && { skip: options.offset }),
    };

    // 添加时间范围查询
    if (options?.startDate || options?.endDate) {
      const whereConditions = queryOptions.where as any;
      if (options.startDate && options.endDate) {
        whereConditions.createdAt = {
          $gte: options.startDate,
          $lte: options.endDate,
        };
      } else if (options.startDate) {
        whereConditions.createdAt = { $gte: options.startDate };
      } else if (options.endDate) {
        whereConditions.createdAt = { $lte: options.endDate };
      }
    }

    return this.logsRepository.find(queryOptions);
  }

  /**
   * 聚合查询用户日志 - 按结果状态分组
   * @param userId 用户ID
   */
  async findLogsByGroup(userId: number) {
    return this.logsRepository
      .createQueryBuilder("logs")
      .select("logs.result", "result")
      .addSelect("COUNT(logs.result)", "count")
      .leftJoinAndSelect("logs.user", "user")
      .where("logs.userId = :userId", { userId })
      .groupBy("logs.result")
      .orderBy("logs.result", "DESC")
      .getRawMany();
  }

  /**
   * 聚合查询用户日志 - 按日志级别分组
   * @param userId 用户ID
   */
  async findLogsByLevel(userId: number) {
    return this.logsRepository
      .createQueryBuilder("logs")
      .select("logs.level", "level")
      .addSelect("COUNT(logs.level)", "count")
      .where("logs.userId = :userId", { userId })
      .groupBy("logs.level")
      .orderBy("logs.level", "ASC")
      .getRawMany();
  }

  /**
   * 聚合查询用户日志 - 按日志类型分组
   * @param userId 用户ID
   */
  async findLogsByType(userId: number) {
    return this.logsRepository
      .createQueryBuilder("logs")
      .select("logs.logType", "logType")
      .addSelect("COUNT(logs.logType)", "count")
      .where("logs.userId = :userId", { userId })
      .groupBy("logs.logType")
      .orderBy("logs.logType", "ASC")
      .getRawMany();
  }

  /**
   * 获取系统日志统计
   */
  async getLogStatistics() {
    const [totalLogs, errorLogs, warnLogs, httpLogs, systemLogs] = await Promise.all([
      this.logsRepository.count(),
      this.logsRepository.count({ where: { level: LogLevel.ERROR } }),
      this.logsRepository.count({ where: { level: LogLevel.WARN } }),
      this.logsRepository.count({ where: { logType: LogType.HTTP } }),
      this.logsRepository.count({ where: { logType: LogType.SYSTEM } }),
    ]);

    return {
      totalLogs,
      errorLogs,
      warnLogs,
      httpLogs,
      systemLogs,
      errorRate: totalLogs > 0 ? ((errorLogs / totalLogs) * 100).toFixed(2) : "0.00",
    };
  }

  /**
   * 清理过期日志
   * @param days 保留天数
   */
  async cleanupOldLogs(days: number = 30): Promise<number> {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - days);

    const result = await this.logsRepository
      .createQueryBuilder()
      .delete()
      .where("createdAt < :cutoffDate", { cutoffDate })
      .execute();

    await this.createSystemLog(`清理了 ${result.affected} 条超过 ${days} 天的日志记录`, LogLevel.INFO, "LogCleanup");

    return result.affected || 0;
  }
}
